#include "QuakeMeshLoaders.hpp"

#include <SDL/SDL_types.h>
#include <vector>
#include <list>


using namespace cge;

namespace cge {
	namespace bsp {
		struct Point3s { Sint16 x,y,z; };

		struct Edge    { Uint16 a,b; };

		struct Lump    {
			Uint32 offset;
			Uint32 length;
		};

		struct Header  {
			char magic[4];
			Uint32 version;
			Lump lump[19];
		};

		struct Face {
			Uint16 plane;
			Uint16 plane_side;

			Uint32 first_edge;
			Uint16 num_edges;

			Uint16 texture_info;

			Uint8  lightmap_styles[4];
			Uint32 lightmap_offset;
		};
	}
}


BspMeshLoader::BspMeshLoader( std::istream& is ): is_(is) {}


void BspMeshLoader::create( MeshData &data ) const {
	using namespace bsp;
	Header hdr;
	is_.seekg(0);
	is_.read((char*)&hdr, sizeof(hdr));
	assert( hdr.magic[0] == 'I' && 
		    hdr.magic[1] == 'B' &&
			hdr.magic[2] == 'S' &&
			hdr.magic[3] == 'P' );

	assert( hdr.version == 38 );

	// read verts
	float scaling = 1.0f/40.0f;
	data.verts.realloc( hdr.lump[2].length / 12 );
	is_.seekg( hdr.lump[2].offset );
	is_.read( (char*)data.verts.get(), hdr.lump[2].length );
	
	vec3f center;
	int nVerts = data.verts.size();
	for(int i=0; i<nVerts; ++i) {
		data.verts[i] *= scaling;
		center += data.verts[i] / (float)nVerts;
	}

	for( int i=0; i<nVerts; ++i )
		data.verts[i] -= center;

	// read edges
	DynamicArray<Edge> edges(hdr.lump[11].length / 4);
	is_.seekg(hdr.lump[11].offset);
	is_.read( (char*)edges.get(), hdr.lump[11].length );

	// read faceEdges
	DynamicArray<Sint32> faceEdges(hdr.lump[12].length / 4);
	is_.seekg(hdr.lump[12].offset);
	is_.read( (char*)faceEdges.get(), hdr.lump[12].length );

	// read faces
	DynamicArray<Face> faces( hdr.lump[6].length / sizeof(Face) );
	int nFaces = faces.size();
	
	is_.seekg(hdr.lump[6].offset);
	is_.read( (char*)faces.get(), hdr.lump[6].length );

	std::vector<unsigned>     fvs;
	std::vector<Mesh::Poly>   polys;
	std::vector<vec3f> normals;
	polys.reserve(nFaces*3);
	normals.reserve(nFaces*3);
	fvs.reserve(16);
	for(int i=0; i<nFaces; ++i) {
		fvs.clear();
		int first_edge = faces[i].first_edge;
		int end_edge   = faces[i].first_edge + faces[i].num_edges;
		
		if(faceEdges[first_edge]<0)
			fvs.push_back( edges[-faceEdges[first_edge]].b );
		else
			fvs.push_back( edges[faceEdges[first_edge]].a );
		
		for(int fei=first_edge; fei < end_edge; ++fei ) {
			unsigned v;
			if(faceEdges[fei] < 0)
				v = edges[-faceEdges[fei]].a;
			else
				v = edges[faceEdges[fei]].b;
				
			fvs.push_back( v );
		}

		vec3f      n;
		Mesh::Poly poly;
		for(unsigned vi=1;vi<fvs.size()-2;++vi) {
			vec3f &v1 = data.verts[fvs[0]];
			vec3f &v2 = data.verts[fvs[vi]];
			vec3f &v3 = data.verts[fvs[vi+1]];
			
			n  = -(v1-v2).cross(v2-v3);
			n.normalize();

			poly.vertex[0] = fvs[0];
			poly.vertex[1] = fvs[vi];
			poly.vertex[2] = fvs[vi+1];
			poly.normal[0] = poly.normal[1] = poly.normal[2] = normals.size();
			polys.push_back( poly );
			normals.push_back(n);
		}
	}
	data.polys.fromCollection( polys );
	data.norms.fromCollection( normals );
}